package ch.nostromo.tiffanys.clients.desktop.gui.board;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JOptionPane;

import uk.ac.cam.cl.projectcharlie2010.clients.desktop.controller.DesktopController;
import uk.ac.cam.cl.projectcharlie2010.clients.desktop.gui.Desktop;

import ch.nostromo.lib.controller.NosResourcesException;
import ch.nostromo.lib.swing.NosPanel;
import ch.nostromo.tiffanys.game.board.Board;
import ch.nostromo.tiffanys.game.board.BoardException;
import ch.nostromo.tiffanys.game.board.BoardTools;
import ch.nostromo.tiffanys.game.game.Game;
import ch.nostromo.tiffanys.game.move.Move;
import ch.nostromo.tiffanys.game.move.MoveInput;

public class BoardPanel extends NosPanel {

  private static Logger logger = Logger.getLogger(BoardPanel.class.getName());

  private static final long serialVersionUID = 1L;

  private String boardFrom = null;

  private String boardTo = null;

  private ChessSet chessSet = null;

  private Board currentBoard = null;

  private Desktop desktop;

  private boolean acceptMoveEntry = false;

  private static final String[][] boardCoordinates = new String[][] {

  { "a8", "a7", "a6", "a5", "a4", "a3", "a2", "a1" }, { "b8", "b7", "b6", "b5", "b4", "b3", "b2", "b1" }, { "c8", "c7", "c6", "c5", "c4", "c3", "c2", "c1" }, { "d8", "d7", "d6", "d5", "d4", "d3", "d2", "d1" },
      { "e8", "e7", "e6", "e5", "e4", "e3", "e2", "e1" }, { "f8", "f7", "f6", "f5", "f4", "f3", "f2", "f1" }, { "g8", "g7", "g6", "g5", "g4", "g3", "g2", "g1" }, { "h8", "h7", "h6", "h5", "h4", "h3", "h2", "h1" }

  };

  public BoardPanel(Desktop desktop) throws NosResourcesException {
    this.desktop = desktop;
    initGUI();
    initNos();
  }

  public BoardPanel()  {
    // Do nothing, as only used by GUI Builder
  }

  private void initGUI() {
    {
      this.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent evt) {
          rootMouseClicked(evt);
        }
      });
    }
  }

  public void initNos() throws NosResourcesException {
    chessSet = new ChessSet(this);
  }

  private void paintBoard(Graphics g, int boardSize) {
    int squareSize = boardSize / 8;

    g.setColor(new Color(247, 247, 245));
    g.fillRect(0, 0, getBoardSize(), getBoardSize());
    g.setColor(new Color(249, 134, 89));

    for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++) {
        if ((i % 2 != 0) && (j % 2 == 0)) {
          g.fillRect(i * squareSize, j * squareSize, squareSize, squareSize);
        } else if ((i % 2 == 0) && (j % 2 != 0)) {
          g.fillRect(i * squareSize, j * squareSize, squareSize, squareSize);
        }
      }
    }
  }

  private void paintPiece(Graphics g, int boardSize, int x, int y, int pieceCode, int color) {
    int squareSize = boardSize / 8;

    int origPieceSize = 40;
    int pieceIdx = 0;
    if (pieceCode == Board.PIECE_PAWN) {
      pieceIdx = 0;
    } else if (pieceCode == Board.PIECE_BISHOP) {
      pieceIdx = 1;
    } else if (pieceCode == Board.PIECE_KNIGHT) {
      pieceIdx = 2;
    } else if (pieceCode == Board.PIECE_ROOK) {
      pieceIdx = 3;
    } else if (pieceCode == Board.PIECE_QUEEN) {
      pieceIdx = 4;
    } else if (pieceCode == Board.PIECE_KING) {
      pieceIdx = 5;
    }

    int pieceIdy = 0;
    if (color == Game.COLOR_WHITE) {
      pieceIdy = 1;
    } else {
      pieceIdy = 0;
    }

    BufferedImage pieceGraphics = chessSet.getSubimage(pieceIdx * origPieceSize, pieceIdy * origPieceSize, origPieceSize, origPieceSize);

    BufferedImage bi = resize(pieceGraphics, squareSize - 4, squareSize - 4);

    g.drawImage(bi, squareSize * x + 2, squareSize * y + 2, this);

  }

  private void paintPieces(Graphics g, int boardSize) {

    try {
      if (currentBoard == null) {
        return;
      }

      for (int x = 0; x < 8; x++) {
        for (int y = 0; y < 8; y++) {
          String field = boardCoordinates[x][y];

          if (!BoardTools.isEmptyField(currentBoard, field)) {
            int pieceCode = BoardTools.getPiece(currentBoard, field);
            int color = BoardTools.getPieceColor(currentBoard, field);
            paintPiece(g, boardSize, x, y, pieceCode, color);
          }

        }
      }
    } catch (BoardException e) {
      e.printStackTrace();
    }

  }

  private void paintSelection(Graphics g, int boardSize) {
    if (boardFrom != null) {

      int squareSize = boardSize / 8;

      int selx = 0;
      int sely = 0;

      for (int x = 0; x < 8; x++) {
        for (int y = 0; y < 8; y++) {
          if (boardCoordinates[x][y].equalsIgnoreCase(boardFrom)) {
            selx = x;
            sely = y;
          }
        }
      }

      g.setColor(Color.BLUE);
      g.drawRect(selx * squareSize, sely * squareSize, squareSize, squareSize);
      g.drawRect(selx * squareSize + 1, sely * squareSize + 1, squareSize - 2, squareSize - 2);
    }

    if (boardTo != null) {

      int squareSize = boardSize / 8;

      int selx = 0;
      int sely = 0;

      for (int x = 0; x < 8; x++) {
        for (int y = 0; y < 8; y++) {
          if (boardCoordinates[x][y].equalsIgnoreCase(boardTo)) {
            selx = x;
            sely = y;
          }
        }
      }

      g.setColor(Color.BLUE);
      g.drawRect(selx * squareSize, sely * squareSize, squareSize, squareSize);
      g.drawRect(selx * squareSize + 1, sely * squareSize + 1, squareSize - 2, squareSize - 2);
    }

  }

  @Override
  public void paintComponent(Graphics g) {

    int boardSize = getBoardSize();

    paintBoard(g, boardSize);
    paintSelection(g, boardSize);
    paintPieces(g, boardSize);

  }

  public static BufferedImage resize(BufferedImage image, int newWidth, int newHeight) {
    BufferedImage result = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = result.createGraphics();
    double scaleX = newWidth / (double) image.getWidth();
    double scaleY = newHeight / (double) image.getHeight();
    AffineTransform xform = AffineTransform.getScaleInstance(scaleX, scaleY);
    g.drawRenderedImage(image, xform);
    g.dispose();
    return result;
  }

  void rootMouseClicked(MouseEvent evt) {
    try {
      if (acceptMoveEntry) {

        // Next move, remove old values if needed
        if (boardTo != null) {
          boardFrom = null;
          boardTo = null;
        }

        int x = evt.getX();
        int y = evt.getY();

        int boardSize = getBoardSize();
        int squareSize = boardSize / 8;

        x = x / squareSize;
        y = y / squareSize;

        if (boardFrom == null) {
          boardFrom = boardCoordinates[x][y];
        } else {
          boardTo = boardCoordinates[x][y];
          MoveInput move = new MoveInput(boardFrom, boardTo);

          // Promotion move ?
          if (desktop.isPromotionMove(move)) {
            String[] possiblePromotions = { DesktopController.getInstance().getTranslation("Common.Queen"), DesktopController.getInstance().getTranslation("Common.Rook"), DesktopController.getInstance().getTranslation("Common.Bishop"),
                DesktopController.getInstance().getTranslation("Common.Knight") };

            String defaultPromotion = DesktopController.getInstance().getTranslation("Common.Queen");
            String title = DesktopController.getInstance().getTranslation("Common.Promotion");
            String msg = DesktopController.getInstance().getTranslation("Common.SelectPromotion");

            int response = JOptionPane.showOptionDialog(null // Center in window.
                , msg // Message
                , title // Title in titlebar
                , JOptionPane.YES_NO_OPTION // Option type
                , JOptionPane.PLAIN_MESSAGE // messageType
                , null // Icon (none)
                , possiblePromotions // Button text as above.
                , defaultPromotion // Default button's label
                );

            switch (response) {
            case 0:
              move.setPromotion(Board.PIECE_QUEEN);
              break;
            case 1:
              move.setPromotion(Board.PIECE_ROOK);
              break;
            case 2:
              move.setPromotion(Board.PIECE_BISHOP);
              break;
            case 3:
              move.setPromotion(Board.PIECE_KNIGHT);
              break;
            case -1:
              // Do nothing, board stays
              return;
            default:
              move.setPromotion(Board.PIECE_QUEEN);
            }

          }

          desktop.enterMove(move);
        }
        repaint();
      }
    } catch (NosResourcesException e) {
      logger.log(Level.SEVERE, "Resources error", e);
    }
  }

  public void setBoard(Board board) {
    this.currentBoard = board;
    repaint();
  }

  public void enteredMoveAccepted() {
    repaint();
  }

  public void enteredMoveRejected() {
    this.boardFrom = null;
    this.boardTo = null;
    repaint();
  }

  public void notifyComputeNextMoveFinished(Board board, Move move) {
    this.boardFrom = move.getFrom();
    this.boardTo = move.getTo();
    setBoard(board);

  }

  private int getBoardSize() {
    int boardSize = this.getWidth();
    int height = this.getHeight();
    if (height < boardSize) {
      boardSize = height;
    }
    return boardSize;
  }

  public void closeInterface() {
    acceptMoveEntry = false;
  }

  public void openInterface() {
    acceptMoveEntry = true;
  }

}
